package com.smartglass.alfareallive;

import android.app.Activity;
import android.hardware.Camera;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.hardware.Camera.Parameters;
import android.media.CamcorderProfile;
import android.media.MediaRecorder;
import android.media.AudioManager;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.os.BatteryManager;
import android.os.Environment;
import android.util.Log;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.provider.Settings;
import android.provider.Settings.SettingNotFoundException;
import android.view.WindowManager;

import java.io.IOException;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStreamReader;

import android.widget.Toast;
import com.ingenic.glass.api.touchboard.GestureDetector;
import com.alfareal.glass.camera.*;
import com.alfareal.glass.camera.util.Util;

import org.json.JSONException;
import org.json.JSONObject;

public class CameraRTMPLive extends Activity implements SurfaceHolder.Callback,
        MediaRecorder.OnErrorListener, MediaRecorder.OnInfoListener {
    private static final String BROADCAST_DISMISS_SHAREDATA_ACTION = "com.alfareal.sharedismiss";
    private static final int LVIE_ACTIVITY_REQUEST_CODE = 0;
    private final String TAG = "CameraRTMPLive";
    private final boolean NO_SCREEN_DEVICE = CameraAppImpl.NO_SCREEN_DEVICE;
    // Send message to GlassAVServer.apk
    private final String CAMERA_ACTION_STATUS = "com.ingenic.glass.camera.rtmplive.STATUS";

    private final String CAMERA_RTMP_LIVE_START = "com.ingenic.glass.camera.CameraRTMPLive.start";
    private final String CAMERA_RTMP_LIVE_END = "com.ingenic.glass.camera.CameraRTMPLive.end";
    private final String CAMERA_RTMP_LIVE_PLAY_TTS = "com.ingenic.glass.camera.CameraRTMPLive.playtts";
    private final String LIVE_PARAM = "alfa_live_param";

    // JNI message, consistent with definition in
    // frameworks/av/media/libstagefright/LiveWriter.cpp
    private final int MEDIA_RECORDER_TRACK_INFO_LIVE_SERVER_START = 1900;
    private final int MEDIA_RECORDER_TRACK_INFO_LIVE_DropFrameInterval = 1999; //set frame rate
	private final int MEDIA_RECORDER_TRACK_INFO_LIVE_STATUS = 2000;
    private final CameraErrorCallback mErrorCallback = new CameraErrorCallback();
    private final int OUTPUT_FORMAT_LIVE_RTMP = 12; // see media/mediarecorder.h
    private static CameraRTMPLive mInstance = null;
    private ComboPreferences mPreferences;
    private SurfaceHolder mSurfaceHolder = null;
    private boolean mOpenCameraFail = false;
    private MediaRecorder mMediaRecorder;
    private boolean mMediaRecorderRecording = false;
    // The video file that the hardware camera is about to record into
    // (or is recording into.)
    private CamcorderProfile mProfile;
    private boolean mCaptureTimeLapse = false;
    // Default 0. If it is larger than 0, the camcorder is in time lapse mode.
    private int mTimeBetweenTimeLapseFrameCaptureMs = 0;
    private int mDesiredPreviewWidth;
    private int mDesiredPreviewHeight;
    private Parameters mParameters;
    private int mCameraId;
    private GestureDetector mGestureDetector;
    private Object mLock = new Object();
    private boolean mFinished = false;
    private Camera mCameraDevice;

    private LiveParamUtils mLiveParamUtils;

    // check incall state and add tts
    private AudioManager mAudioManager;
//    private CameraAppImpl mApplication;
    private String mCurrentErrorState;

    private String mFinishReason;

    private Handler mHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            if (CameraAppImpl.DEBUG)
                Log.d(TAG, "handleMessage in msg.what=" + msg.what);
            switch (msg.what) {
                case CameraAppImpl.CAMERA_ERROR_PREVIEW: {
                    mCurrentErrorState = getString(R.string.camera_open_error);
                    setResult(LVIE_ACTIVITY_REQUEST_CODE);
                    mFinishReason = mCurrentErrorState;
                    finish();
                    break;
                }
            }
        }
    };

    private BroadcastReceiver mReceiver = null;

    private class MyBroadcastReceiver extends BroadcastReceiver {
        @Override
        public void onReceive(Context context, Intent intent) {
            if (intent.getAction().equals(Intent.ACTION_BATTERY_CHANGED)) {

                int currentBatteryVoltage =
                        intent.getIntExtra(BatteryManager.EXTRA_VOLTAGE,CameraAppImpl.LOWEST_BATTERY_VOLTAGE);
                int currentBatteryLevel =
                        intent.getIntExtra(BatteryManager.EXTRA_LEVEL,CameraAppImpl.LOWEST_BATTERY_LEVEL);
                if (CameraAppImpl.DEBUG)
                {
                    Log.d(TAG, "battery has change :: currentBatteryVoltage= " + currentBatteryVoltage);
                    Log.d(TAG, "battery has change :: currentBatteryLevel= " + currentBatteryLevel);
                }

                if (currentBatteryVoltage <= CameraAppImpl.LOWEST_BATTERY_VOLTAGE &&
                        currentBatteryLevel <= CameraAppImpl.LOWEST_BATTERY_LEVEL) {
                    mCurrentErrorState = getString(R.string.live_error_low_battery);
                    CameraRTMPLive.this.setResult(LVIE_ACTIVITY_REQUEST_CODE);
                    mFinishReason = mCurrentErrorState;
                    finish();
                }
            }
        }
    }

    @Override
    public void onCreate(Bundle icicle) {
        super.onCreate(icicle);
        Settings.System.putString(this.getContentResolver(), "phone_state","idle");
        if (CameraAppImpl.DEBUG)
            Log.d(TAG, "onCreate in");
        CameraRTMPLive.setMInstance(this);
        mAudioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
        mCurrentErrorState = getString(R.string.live_no_error);
        if (!checkEnvironmentState()) {
            setResult(LVIE_ACTIVITY_REQUEST_CODE);
            mFinishReason = mCurrentErrorState;
            finish();
            return;
        }
        setContentView(R.layout.camera_live);
        SurfaceView sv = (SurfaceView) findViewById(R.id.surfaceview);
        SurfaceHolder holder = sv.getHolder();
        holder.addCallback(this);
        holder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
        mGestureDetector = new GestureDetector(this,
                new MySimpleGestureDetectorListener());
        mMediaRecorder = new MediaRecorder();
        mMediaRecorder.setOnErrorListener(this);
        mMediaRecorder.setOnInfoListener(this);
//        mApplication = (CameraAppImpl) getApplication();
        init();
        if (mOpenCameraFail) {
            mCurrentErrorState = getString(R.string.camera_open_error);
            setResult(LVIE_ACTIVITY_REQUEST_CODE);
            mFinishReason = mCurrentErrorState;
            finish();
            return;
        }
        Settings.System.putString(this.getContentResolver(), "camera_state",
                "live");
    }

    @Override
    public void onResume() {
        super.onResume();
        Log.d(TAG, "onResume");
    }

    @Override
    public void onPause() {
        super.onPause();
        Log.d(TAG, "onPause");

        String phone_state = Settings.System.getString(this.getContentResolver(), "phone_state");
        Log.d(TAG, "phone_state = " + phone_state);
        if (phone_state !=null && phone_state.equals("incall")) {
            Log.d(TAG, "in call,finish live");
            Settings.System.putString(this.getContentResolver(), "phone_state","idle");
            mCurrentErrorState = getString(R.string.live_no_error);
            setResult(LVIE_ACTIVITY_REQUEST_CODE);
            mFinishReason = "in call";
            finish();
        }

    }

    @Override
    public void onStop(){
        super.onStop();
        Log.d(TAG, "onStop");
    }

    private boolean checkEnvironmentState() {
        int currentBatteryVoltage = 0;
        try {
            currentBatteryVoltage = Settings.System.getInt(
                    getContentResolver(), "batteryVoltage");
        } catch (SettingNotFoundException e) {
            Log.e(TAG, "batteryVoltage -- SettingNotFound");
        }

        if (currentBatteryVoltage <= CameraAppImpl.LOWEST_BATTERY_VOLTAGE) {
            mCurrentErrorState = getString(R.string.live_error_low_battery);
            return false;
        } else if (mAudioManager.getMode() == AudioManager.MODE_IN_CALL) {
            mCurrentErrorState = getString(R.string.not_support_live_with_audio);
            return false;
        }
        return true;
    }

    private void init() {
        Util.initializeScreenBrightness(getWindow(), getContentResolver());
        mPreferences = new ComboPreferences(this);
        CameraSettings.upgradeGlobalPreferences(mPreferences.getGlobal());
        mCameraId = CameraSettings.readPreferredCameraId(mPreferences);
        if (CameraAppImpl.DEBUG)
            Log.d(TAG, "---mCameraId=" + mCameraId);
        // Testing purpose. Launch a specific camera through the intent extras.
        int intentCameraId = Util.getCameraFacingIntentExtras(this);
        if (intentCameraId != -1) {
            mCameraId = intentCameraId;
        }

        mPreferences.setLocalId(this, mCameraId);
        CameraSettings.upgradeLocalPreferences(mPreferences.getLocal());

        // Register BroadcastReceiver to handle some event
        IntentFilter intentFilter = new IntentFilter(
                Intent.ACTION_BATTERY_CHANGED);
        mReceiver = new MyBroadcastReceiver();
        registerReceiver(mReceiver, intentFilter);

		/*
         * To reduce startup time, we start the preview in another thread. We
		 * make sure the preview is started at the end of onCreate.
		 */
        Thread startPreviewThread = new Thread(new Runnable() {
            public void run() {
                try {
                    if (CameraAppImpl.DEBUG)
                        Log.d(TAG, "---opencamera mCameraId=" + mCameraId);
                    mCameraDevice = Util.openCamera(CameraRTMPLive.this,
                            mCameraId);
                    if (mCameraDevice == null) {
                        mOpenCameraFail = true;
                    } else {
                        mCameraDevice.setErrorCallback(mErrorCallback);
                        mErrorCallback.setHandler(mHandler);
                    }
                } catch (Exception e) {
                    mOpenCameraFail = true;
                }
            }
        });
        startPreviewThread.start();
        Util.enterLightsOutMode(getWindow());
        // Make sure preview is started.
        try {
            startPreviewThread.join();
        } catch (InterruptedException ex) {
            // ignore
        }
    }

    private void readVideoPreferences(boolean hasAudio) {
        // The preference stores values from ListPreference and is thus string
        // type for all values.
        // We need to convert it to int manually.
        String defaultQuality = CameraSettings.getDefaultVideoQuality(
                mCameraId,
                getResources().getString(R.string.pref_video_quality_default));
        String videoQuality = mPreferences.getString(
                CameraSettings.KEY_VIDEO_QUALITY, defaultQuality);
        int quality = Integer.valueOf(videoQuality);
        if (!hasAudio) {
            mTimeBetweenTimeLapseFrameCaptureMs = 40;
            mCaptureTimeLapse = true;
        } else {
            mTimeBetweenTimeLapseFrameCaptureMs = 0;
            mCaptureTimeLapse = false;
        }
        // TODO: This should be checked instead directly +1000.
        if (mCaptureTimeLapse)
            quality += 1000;
        mProfile = CamcorderProfile.get(mCameraId, quality);
        // mProfile.fileFormat = OUTPUT_FORMAT_LIVE_RTMP;
        getDesiredPreviewSize();
    }

    private void getDesiredPreviewSize() {
        if (mCameraDevice == null)
            return;
        mParameters = mCameraDevice.getParameters();
        mDesiredPreviewWidth = mProfile.videoFrameWidth;
        mDesiredPreviewHeight = mProfile.videoFrameHeight;

        mDesiredPreviewWidth = mLiveParamUtils.Alfa_videoformat_W;//768;
        mDesiredPreviewHeight = mLiveParamUtils.Alfa_videoformat_H;//432;
    }

    private void closeCamera() {
        if (CameraAppImpl.DEBUG)
            Log.d(TAG, "closeCamera");
        if (mCameraDevice == null) {
            return;
        }
        mCameraDevice.stopPreview();
        CameraHolder.instance().release();
        mCameraDevice.setErrorCallback(null);
        mCameraDevice = null;
    }

    private void finishRecorderAndCloseCamera() {
        if (CameraAppImpl.DEBUG)
            Log.d(TAG, "-------stopVideoRecording in");
        stopVideoRecording();
        closeCamera();
    }

    @Override
    public boolean onKeyDown(int keyCode, KeyEvent event) {
        /*if (keyCode == KeyEvent.KEYCODE_VOLUME_UP ||
                keyCode == KeyEvent.KEYCODE_VOLUME_DOWN){
            return true;
        }*/
        return super.onKeyDown(keyCode, event);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        if (CameraAppImpl.DEBUG)
            Log.d(TAG, "onDestroy in");
        Settings.System.putString(this.getContentResolver(), "camera_state", "");
        Intent intent = new Intent(CAMERA_ACTION_STATUS);
        intent.putExtra("status", mCurrentErrorState);
        sendBroadcast(intent);
//        if (mApplication != null)
//            mApplication.releaseWakeLock();
        if (mReceiver != null) {
            unregisterReceiver(mReceiver);
            mReceiver = null;
        }
        CameraRTMPLive.setMInstance(null);
    }

    // Prepares media recorder.
    private void initializeRecorder() {
        // If the mCameraDevice is null, then this activity is going to finish
        if (CameraAppImpl.DEBUG)
            Log.d(TAG, "mCameraDevice = " + mCameraDevice
                    + " mSurfaceHolder = " + mSurfaceHolder);
        if (mCameraDevice == null || mSurfaceHolder == null)
            return;
        // Unlock the camera object before passing it to media recorder.
        mCameraDevice.unlock();
        mMediaRecorder.setCamera(mCameraDevice);
        if (!mCaptureTimeLapse) {
            mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.CAMCORDER);
        }

        // mMediaRecorder.setVideoSource(MediaRecorder.VideoSource.CAMERA);
        // mMediaRecorder.setProfile(mProfile);

        mMediaRecorder.setVideoSource(MediaRecorder.VideoSource.CAMERA);
        mMediaRecorder.setOutputFormat(OUTPUT_FORMAT_LIVE_RTMP);
        mMediaRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.H264);
        mMediaRecorder.setVideoFrameRate(mLiveParamUtils.Alfa_framerate);//(15);
        mMediaRecorder.setVideoSize(mDesiredPreviewWidth, mDesiredPreviewHeight);
        mMediaRecorder.setVideoEncodingBitRate(10000000);
        mMediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC);
        mMediaRecorder.setAudioEncodingBitRate(12200);
        mMediaRecorder.setAudioChannels(2);
        mMediaRecorder.setAudioSamplingRate(16000);

        if (mCaptureTimeLapse) {
            mMediaRecorder.setCaptureRate((1000 / (double) mTimeBetweenTimeLapseFrameCaptureMs));
        }
        mMediaRecorder.setOutputFile(Environment.getExternalStorageDirectory()
                .toString() + '/' + "camerartmplive");
        mMediaRecorder.setPreviewDisplay(mSurfaceHolder.getSurface());
        try {
            mMediaRecorder.prepare();
        } catch (IOException e) {
            releaseMediaRecorder();
            throw new RuntimeException(e);
        }
    }

    private void releaseMediaRecorder() {
        if (mMediaRecorder != null) {
            mMediaRecorder.reset();
            mMediaRecorder.release();
            mMediaRecorder = null;
        }
    }


    // from MediaRecorder.OnErrorListener
    @Override
    public void onError(MediaRecorder mr, int what, int extra) {
        Log.e(TAG, "MediaRecorder error. what=" + what + " extra=" + extra);
        mCurrentErrorState = getString(R.string.video_record_error);
        setResult(LVIE_ACTIVITY_REQUEST_CODE);
        mFinishReason = mCurrentErrorState;
        finish();
    }

    // from MediaRecorder.OnInfoListener
    @Override
    public void onInfo(MediaRecorder mr, int what, int extra) {
        Log.e(TAG, "MediaRecorder info. what=" + what + " extra=" + extra);

        if (what == MediaRecorder.MEDIA_RECORDER_INFO_MAX_DURATION_REACHED) {
            if (mMediaRecorderRecording)
                stopVideoRecording();
        } else if (what == MediaRecorder.MEDIA_RECORDER_INFO_MAX_FILESIZE_REACHED) {
            if (mMediaRecorderRecording)
                stopVideoRecording();
        } else if (what == MEDIA_RECORDER_TRACK_INFO_LIVE_SERVER_START) {
            Intent intentStatus = new Intent(CAMERA_ACTION_STATUS);
            intentStatus.putExtra("status", "start_live");
            sendBroadcast(intentStatus);
        } else if (what == MEDIA_RECORDER_TRACK_INFO_LIVE_DropFrameInterval) {
            Log.e(TAG, "MediaRecorder setVideoDropFrameInterval,framerate="  + extra);
//            mMediaRecorder.setVideoDropFrameInterval(extra);
		} else if (what == MEDIA_RECORDER_TRACK_INFO_LIVE_STATUS) {
			Log.e(TAG, "MEDIA_RECORDER_TRACK_INFO_LIVE_STATUS,STATUS="  + extra);
        }
    }

    private void startVideoRecording() {
        if (mMediaRecorderRecording || mFinished)
            return;
        mLiveParamUtils = (LiveParamUtils)getIntent().getSerializableExtra("liveParam");
		
        readVideoPreferences(mAudioManager.getMode() != AudioManager.MODE_IN_CALL);
        setCameraParameters();
        if (CameraAppImpl.DEBUG)
            Log.d(TAG, "startVideoRecording");

        initializeRecorder();
        if (mMediaRecorder == null) {
            Log.e(TAG, "Fail to initialize media recorder");
            return;
        }
        try {
            mMediaRecorder.start(); // Recording is now started
        } catch (RuntimeException e) {
            Log.e(TAG, "Could not start media recorder. ", e);
            releaseMediaRecorder();
            // If start fails, frameworks will not lock the camera for us.
            mCameraDevice.lock();
            return;
        }
        mMediaRecorderRecording = true;

        Intent intent = new Intent();
        intent.setAction(CAMERA_RTMP_LIVE_START);
        this.sendBroadcast(intent);

        Intent intent_tts = new Intent();
        intent_tts.setAction(CAMERA_RTMP_LIVE_PLAY_TTS);
        intent_tts.putExtra("tts",getString(R.string.tts_live_video_record_start));
        this.sendBroadcast(intent_tts);
    }

    private void stopVideoRecording() {
        if (CameraAppImpl.DEBUG)
            Log.d(TAG, "stopVideoRecording");

        Intent intent = new Intent();
        intent.setAction(CAMERA_RTMP_LIVE_END);
        this.sendBroadcast(intent);

        if (mMediaRecorderRecording) {
            try {
                mMediaRecorder.setOnErrorListener(null);
                mMediaRecorder.setOnInfoListener(null);
                mMediaRecorder.stop();
            } catch (RuntimeException e) {
                Log.e(TAG, "stop fail", e);
            }
            mMediaRecorderRecording = false;

        }
        releaseMediaRecorder();
    }

    @Override
    public void finish() {
        Log.d(TAG, "finish reason =" + mFinishReason);
        synchronized (mLock) {
            if (mFinished)
                return;
            mFinished = true;
        }
        // 及时closecamera，否则第二次开启会失败，
        // 原先是在onDestroy中做，而onDestroy调用较晚如果在onDestroy
        // 调用之前开启录像就会出错，所以把closecamera提前到finish时做
        finishRecorderAndCloseCamera();
        if (CameraAppImpl.USE_TTS && mAudioManager.getMode() != AudioManager.MODE_IN_CALL) {
            String tts;
            if (mCurrentErrorState.equals(getString(R.string.live_no_error))) {
                tts = getString(R.string.tts_live_video_record_stop);
            }
            else if(mCurrentErrorState.equals(getString(R.string.live_error_low_battery))){
                tts = getString(R.string.tts_live_video_lowpower);
            }
            else if(mCurrentErrorState.equals(getString(R.string.camera_open_error))){
                tts = getString(R.string.tts_camera_fail);
            }
            else {
                tts = getString(R.string.tts_live_video_record_error);
            }

            Intent intent_tts = new Intent();
            intent_tts.setAction(CAMERA_RTMP_LIVE_PLAY_TTS);
            intent_tts.putExtra("tts",tts);
            this.sendBroadcast(intent_tts);
        }

        super.finish();
    }

    private void setCameraParameters() {
        if (CameraAppImpl.DEBUG)
            Log.d(TAG, "set camera_ipu_direct record and restart preview.");
        if (mCameraDevice == null)
            return;
        if (NO_SCREEN_DEVICE)
            mParameters.set("preview_mode", CameraAppImpl.NO_SCREEN_LIVE);
        else
            mParameters.set("preview_mode",
                    CameraAppImpl.LOW_POWER_DISPLAY_VIDEO_LIVE);
        mParameters.setPreviewSize(mDesiredPreviewWidth, mDesiredPreviewHeight);
        mCameraDevice.setParameters(mParameters);
//        mApplication.acquireWakeLock();
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        return mGestureDetector.onTouchEvent(event);
    }

    @Override
    public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
        if (CameraAppImpl.DEBUG)
            Log.d(TAG, "holder.getSurface() = " + holder.getSurface()
                    + "  mCameraDevice = " + mCameraDevice);
        if (holder.getSurface() == null || mCameraDevice == null)
            return;
        mSurfaceHolder = holder;
        startVideoRecording();
    }

    @Override
    public void surfaceCreated(SurfaceHolder holder) {
    }

    @Override
    public void surfaceDestroyed(SurfaceHolder holder) {
        if (CameraAppImpl.DEBUG)
            Log.d(TAG, "--surfaceDestroyed");
        mSurfaceHolder = null;
    }

    class MySimpleGestureDetectorListener extends
            GestureDetector.SimpleOnGestureListener {
        @Override
        public boolean onSlideDown(boolean fromPhone) {
            mCurrentErrorState = getString(R.string.live_no_error);
            Intent intent = new Intent();
            intent.setAction(BROADCAST_DISMISS_SHAREDATA_ACTION);
            sendBroadcast(intent);
            setResult(LVIE_ACTIVITY_REQUEST_CODE);
            mFinishReason = "slide down";
            finish();
            return true;
        }
    }

    public synchronized static void setMInstance(CameraRTMPLive mInstance) {
        CameraRTMPLive.mInstance = mInstance;
    }

    public synchronized static CameraRTMPLive getMInstance() {
        return CameraRTMPLive.mInstance;
    }

    public void setErrorState(String state) {
        mCurrentErrorState = state;
    }

}